#include "common.h"
#include "index.h"
#include "lock.h"
#include <list>

static int get_index_twobytes(char *dataIndexDir,char key[2],list<struct index2> &iList);
static int out_one_record(char *dataIndexDir,struct index *pIndex,FILE *fp_out,FILE *fp_out_contain,char *keyword);

/**
 * find out indexes for one keyword
 *
 * @argv[1] dataIndex dir
 * @argv[2] the keyword
 * @argv[3] out put file
 */
int main(int argc,char *argv[]) {

	int i;
	int length;
	list<struct index2> iList;
	char *keyword;
	char *dataIndexDir = argv[1];
	char *out_file = argv[3];
	FILE *fp_out;
	FILE *fp_out_contain;
	FILE *fp_merge;
	int fd_lock;
	char filename_contain[MAXLINE];
	char merge_file[MAXLINE];
	char buf[MAXLINE];

	if((fp_out = fopen(out_file,"w")) == NULL) {
		MSG("fopen failed:%s",argv[3]);
		exit(EXIT_FAILURE);
	}

	snprintf(filename_contain,MAXLINE,"%s_%d",out_file,getpid());
	if((fp_out_contain = fopen(filename_contain,"w")) == NULL) {
		MSG("fopen failed:%s",filename_contain);
		exit(EXIT_FAILURE);
	}
	
	if(argc != 4) {
		MSG("argc error:getIndex");
		exit(EXIT_FAILURE);
	}

	fd_lock = open(LOCK_DATAINDEX,O_RDWR|O_CREAT,0644);
	if(fd_lock < 0) {
		MSG("open failed:"LOCK_DATAINDEX);
		exit(EXIT_FAILURE);
	}
	if(readw_lock(fd_lock,0,SEEK_SET,0) < 0) {
		MSG("readw_lock failed:"LOCK_DATAINDEX);
		exit(EXIT_FAILURE);
	}

	keyword = argv[2];
	length = strlen(keyword);	
	for(i = 0; i < length; i++) {
		if(i == (length-1))
			break;
		
		if((unsigned char)keyword[i] >= 128 && (unsigned char)keyword[i+1] >= 128) {
			if(get_index_twobytes(dataIndexDir,&keyword[i],iList) == -1) {
				MSG("get_index_twobytes failed:c1 = %hhu\tc2=%hhu",
						keyword[i],keyword[i+1]);
			}
			i++;
		}
		else if((unsigned char)keyword[i] < 128 && (unsigned char)keyword[i+1] < 128) {
			if(get_index_twobytes(dataIndexDir,&keyword[i],iList) == -1) {
				MSG("get_index_twobytes failed:c1 = %hhu\tc2=%hhu",
						keyword[i],keyword[i+1]);
			}
		}
		else if((unsigned char)keyword[i] < 128 && (unsigned char)keyword[i+1] >= 128) {
			continue;
		}

	}

	iList.sort();
	fprintf(fp_out,"%d results\n",iList.size());

	list<struct index2>::reverse_iterator it;
	for(it = iList.rbegin(); it != iList.rend(); it++) {
		//printf("count=%u\tsite=%u\tpath=%u\tattr=%u\n",
		//		it->count,it->idx.siteId,it->idx.pathId,it->idx.attrId);
		if(out_one_record(dataIndexDir,&it->idx,fp_out,fp_out_contain,keyword) == -1) {
			MSG("out_one_record failed");
		}
	}

	if(un_lock(fd_lock,0,SEEK_SET,0) < 0) {
		MSG("un_lock failed:"LOCK_DATAINDEX);
		exit(EXIT_FAILURE);
	}
	if(close(fd_lock) < 0) {
		MSG("close failed:"LOCK_DATAINDEX);
		exit(EXIT_FAILURE);
	}

	fclose(fp_out);
	fclose(fp_out_contain);
	
	snprintf(merge_file,MAXLINE,"%s_%d%d",out_file,getpid(),getpid());
	if((fp_merge = fopen(merge_file,"w")) == NULL) {
		MSG("fp_merge failed:%s",merge_file);
		exit(EXIT_FAILURE);
	}

	if((fp_out = fopen(out_file,"r")) == NULL) {
		MSG("fopen failed:%s",out_file);
		exit(EXIT_FAILURE);
	}

	if((fp_out_contain = fopen(filename_contain,"r")) == NULL) {
		MSG("fopen failed:%s",filename_contain);
		exit(EXIT_FAILURE);
	}

	fgets(buf,MAXLINE,fp_out);
	fprintf(fp_merge,"%s",buf);

	while(fgets(buf,MAXLINE,fp_out_contain) != NULL) {
		fprintf(fp_merge,"%s",buf);
	}

	while(fgets(buf,MAXLINE,fp_out) != NULL) {
		fprintf(fp_merge,"%s",buf);
	}

	fclose(fp_merge);
	fclose(fp_out);
	fclose(fp_out_contain);

	if(remove(out_file) < 0) {
		MSG("remove failed:%s",out_file);
		exit(EXIT_FAILURE);
	}

	if(remove(filename_contain) < 0) {
		MSG("remove failed:%s",filename_contain);
		exit(EXIT_FAILURE);
	}

	if(rename(merge_file,out_file) < 0) {
		MSG("rename failed:%s -> %s",merge_file,out_file);
		exit(EXIT_FAILURE);
	}

	run_log("getIndex finished:keyword=%s",keyword);

	exit(EXIT_SUCCESS);
}

static int get_index_twobytes(char *dataIndexDir,char key[2],list<struct index2> &iList) {

	unsigned char c1,c2;
	FILE *fp_index;
	struct index2 idx2;
	char buf[MAXLINE];
	list<struct index2>::iterator it;

	bzero(&idx2,sizeof(idx2));
	idx2.count = 1;
	c1 = key[0];
	c2 = key[1];

	snprintf(buf,MAXLINE,"%s/%s/%hhu/%hhu",dataIndexDir,INDEX_DIR,c1,c2);
	fp_index = fopen(buf,"r");
	if(fp_index == NULL) {
		MSG("fopen failed:%s",buf);
		return 0;
	}

	while(fread(&idx2.idx,sizeof(struct index),1,fp_index) == 1) {
		//printf("siteId=%u\tpathId=%u\tattrId=%u\n",
		//		idx2.idx.siteId,idx2.idx.pathId,idx2.idx.attrId);	
		for(it = iList.begin(); it != iList.end(); it++) {
			if(it->idx.siteId == idx2.idx.siteId && 
					it->idx.attrId == idx2.idx.attrId) {
				it->count++;
				break;
			}
		}

		if(it == iList.end()) {
			iList.push_back(idx2);
		}

	}
	fclose(fp_index);

	return 0;
}

static int out_one_record(char *dataIndexDir,struct index *pIndex,FILE *fp_out,FILE *fp_out_contain,char *keyword) {

	char buf[MAXLINE];
	char path_str[MAXLINE];
	char name_str[MAXLINE];
	char keyword_token[MAXLINE];
	char *token;
	char *delim;
	struct site st;
	struct path pt;
	struct attr at;
	FILE *fp;
	int ret;

	bzero(&st,sizeof(st));
	bzero(&st,sizeof(pt));
	bzero(&st,sizeof(at));

	snprintf(buf,MAXLINE,"%s/%s",dataIndexDir,SITES_FORMATTED);
	if((fp = fopen(buf,"r")) == NULL) {
		MSG("fopen failed:%s",buf);
		return -1;
	}
	if(fseek(fp,sizeof(struct site)*pIndex->siteId,SEEK_SET) != 0) {
		MSG("fseek failed:%s",buf);
		fclose(fp);
		return -1;
	}

	if(fread(&st,sizeof(struct site),1,fp) != 1) {
		MSG("fread failed:%s",buf);
		fclose(fp);
		return -1;
	}
	fclose(fp);

	//printf("site=%u\n%s %hu\n%s %s\n",
	//		st.siteId,st.ip_addr,st.port,st.username,st.password);
	
	//fprintf(fp_out,"ftp://%s:%s@%s:%hu\n",st.username,st.password,st.ip_addr,st.port);
	
	snprintf(buf,MAXLINE,"%s/%s/%u",dataIndexDir,PATH_DIR,st.siteId);
	if((fp = fopen(buf,"r")) == NULL) {
		MSG("fopen failed:%s",buf);
		return -1;
	}
	if(fseek(fp,sizeof(struct path)*pIndex->pathId,SEEK_SET) != 0) {
		MSG("fseek failed:%s",buf);
		fclose(fp);
		return -1;
	}
	if(fread(&pt,sizeof(struct path),1,fp) != 1) {
		MSG("fread failed:%s",buf);
		fclose(fp);
		return -1;
	}
	fclose(fp);

	snprintf(buf,MAXLINE,"%s/%s/%u%s",dataIndexDir,PATH_DIR,st.siteId,PATH_MAP);
	//printf("buf=(%s)\n",buf);
	if((fp = fopen(buf,"r")) == NULL) {
		MSG("fopen failed:%s",buf);
		return -1;
	}
	if(fseek(fp,pt.offset,SEEK_SET) != 0) {
		MSG("fseek failed:%s",buf);
		fclose(fp);
		return -1;
	}

	//bzero(buf,sizeof(buf));
	//if(fgets(buf,MAXLINE,fp) == NULL) {
	if(fgets(path_str,MAXLINE,fp) == NULL) {
		MSG("fgets failed:%s",path_str);
		fclose(fp);
		return -1;
	}
	fclose(fp);
	//printf("%s\n",buf);
	//buf[strlen(buf)-1] = 0;
	//fprintf(fp_out,"%s\n",buf);
	path_str[strlen(path_str)-1] = 0;
	
	snprintf(buf,MAXLINE,"%s/%s/%u",dataIndexDir,ATTR_DIR,st.siteId);
	fp = fopen(buf,"r");
	if(fp == NULL) {
		MSG("fopen failed:%s",buf);
		return -1;
	}
	ret = fseek(fp,sizeof(struct attr)*pIndex->attrId,SEEK_SET);
	if(ret < 0) {
		MSG("fseek failed:%s",buf);
		fclose(fp);
		return -1;
	}
	ret = fread(&at,sizeof(struct attr),1,fp);
	if(ret == 0) {
		MSG("fread failed:%s",buf);
		fclose(fp);
		return -1;
	}
	fclose(fp);

	snprintf(buf,MAXLINE,"%s/%s/%u%s",dataIndexDir,ATTR_DIR,st.siteId,PATH_MAP);
	fp = fopen(buf,"r");
	if(fp == NULL) {
		MSG("fopen failed:%s",buf);
		return -1;
	}
	ret = fseek(fp,at.offset,SEEK_SET);
	if(ret < 0) {
		MSG("fseek failed%s",buf);
		fclose(fp);
		return -1;
	}

	//if(fgets(buf,MAXLINE,fp) == NULL) {
	if(fgets(name_str,MAXLINE,fp) == NULL) {
		MSG("fgets failed");
		fclose(fp);
		return -1;
	}
	fclose(fp);
	//buf[strlen(buf)-1] = 0;
	name_str[strlen(name_str)-1] = 0;

	strncpy(keyword_token,keyword,MAXLINE);
	delim = " \t";
	FILE *fp_final_to = fp_out;

	token = strtok(keyword_token,delim);
	while(token != NULL) {
		//printf("token=(%s)\n",token);
		if(strcasestr(name_str,token) != NULL) {
			//printf("got substr:(%s)\n",token);
			fp_final_to = fp_out_contain;
			break;
		}
		token = strtok(NULL,delim);
		
	}
	
	//printf("%s\n",buf);
	fprintf(fp_final_to,"ftp://%s:%s@%s:%hu\n",st.username,st.password,st.ip_addr,st.port);
	fprintf(fp_final_to,"%s\n",path_str);
	fprintf(fp_final_to,"%s\n",name_str);
	fprintf(fp_final_to,"\n");

	return 0;
}
